/** 
 * <p>Original Author:  jessefreeman</p>
 * <p>Class File: SliderController.as
 * 
 * <p>Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:</p>
 * 
 * <p>The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.</p>
 * 
 * <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.</p>
 * 
 * <p>Licensed under The MIT License</p>
 * <p>Redistributions of files must retain the above copyright notice.</p>
 *
 * <p>Based on the excellent "Throw Slide" by Lee Brimelow example on 
 * <a href="http://www.gotoandlearn.com/">http://www.gotoandlearn.com/</a>.</p>
 * 
 * <p>Revisions<br/> 
 * 	0.1  Initial version Oct 26, 2008</p>
 *
 */

package camo.controllers
{
	import camo.events.ComponentEvent;
	import camo.views.components.AbstractComponent;
	
	import flash.display.DisplayObject;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	public class SliderController extends AbstractController
	{
		private var _bounds:Object;
		private var _currentX:Number;
		private var _lastX:Number;
		private var _vx:Number = 0;
		private var _isDragging:Boolean = false;
		private var _offset:Number;
		private var _thumb:DisplayObject;
		private var _trackAsset:DisplayObject;
		private var _updateTimer:Timer;
		private var _lastPercent:Number = 0;
		private var _thumbPos:Number = 0;
		private var _externalControl:Boolean;
		
		private var _timer:Timer;
				
		/**
		 * 
		 * @return 
		 * 
		 */		
		public function get minVal():Number{
			return _bounds.left;	
		}
		
		/**
		 * 
		 * @return 
		 * 
		 */		
		public function get maxVal():Number{
			return _bounds.right;	
		}
		
		/**
		 * 
		 * @return 
		 * 
		 */		
		public function get maxPos():Number{
			return _trackAsset.width - _thumb.width; 
		}
		
		/**
		 * 
		 * @param v
		 * 
		 */		
		public function setValue(p:Number):void {
			if (_isDragging) return;   // ignore updates during drag
			if (p < 0) p = 0;
			if (p > 1) p = 1;
			var s:Number = _trackAsset.width;
			var t:Number = _thumb.width;
			var tx:Number = _thumb.x;
			var newX:Number = p*(s - t); // + tx;
			_vx = newX;
			_thumb.x = _trackAsset.x + _vx;				
			_externalControl = true;
		}
		
		/**
		 * 
		 * @param target
		 * 
		 */		
		public function SliderController(target:AbstractComponent)
		{
			super(this, target);
		}
		
		/**
		 * 
		 * @param target
		 * 
		 */		
		override protected function init(target:AbstractComponent):void{
			super.init(target);
			
			_timer = new Timer(125);  // only update the slider 8 times a second
			
			_thumb = _observableComponent["thumb"];
			_thumb["buttonMode"] = true;
			_thumb["useHandCursor"] = true;
			
			_currentX = _thumb.x;
			_lastX = _thumb.x;
			_trackAsset = _observableComponent["track"];
			_trackAsset["useHandCursor"] = true;
			_trackAsset["buttonMode"] = true;
			
			_bounds = {left: (_trackAsset.x), right: (_trackAsset.width - _thumb.width)};
			
			_timer.addEventListener(TimerEvent.TIMER,loop,false,0,true);
			_timer.start();

		}
		
		/**
		 * 
		 * 
		 */		
		public function activate():void{
			_thumb.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
			//_observableComponent.addEventListener(MouseEvent.CLICK, onClick);
			_observableComponent.addEventListener(MouseEvent.MOUSE_DOWN, onTrackDown); 
		}
		
		/**
		 * 
		 * 
		 */		
		public function deactivate():void{
			_thumb.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
			_timer.removeEventListener(TimerEvent.TIMER,loop);
			//_observableComponent.removeEventListener(MouseEvent.CLICK, onClick);
			_observableComponent.removeEventListener(MouseEvent.MOUSE_DOWN, onTrackDown);
			//_updateTimer.start();
		}
		
		/**
		 * 
		 * @param e
		 * 
		 */		
		public function onDown(e:MouseEvent):void
		{
			_isDragging = true;
			_offset = _thumb.mouseX;
			
			_observableComponent.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove,false,0,true);
			_observableComponent.stage.addEventListener(MouseEvent.MOUSE_UP, onUp,false,0,true);
			_externalControl = false;
		}
		
		/**
		 * 
		 * @param e
		 * 
		 */		
		public function onUp(e:MouseEvent):void
		{
			_isDragging = false;
			setValue((_thumb.x - _trackAsset.x)/(_trackAsset.width - _thumb.width));
			_observableComponent.stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
			_observableComponent.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
			// throw one more event, repeating the last percentage we dragged to.
			// this is necessary so that higher-level constructs can enable or disable
			// sister controls and toggle switches. 
			dispatchEvent(new ComponentEvent(ComponentEvent.VALUE_UPDATE,{value: _lastPercent, external: _externalControl},true,true));
		}
		
		/**
		 * 
		 * @param e
		 * 
		 */		
		public function onMove(e:MouseEvent):void
		{
			_thumb.x = _observableComponent.mouseX - _offset;
			if(_thumb.x <= _bounds.left)
				_thumb.x = _bounds.left;
			else if(_thumb.x >= _bounds.right)
				_thumb.x = _bounds.right;
			e.updateAfterEvent();
		}
		
		//
		// When a user clicks on the track, center the thumb over the mouse
		//
		public function onClick(e:MouseEvent):void {
			var pct:Number = (_observableComponent.mouseX-_thumb.width*0.5)/(_trackAsset.width - _thumb.width);
			if (pct < 0) pct = 0;
			if (pct > 1) pct = 1;
			dispatchEvent(new ComponentEvent(ComponentEvent.VALUE_UPDATE,{value: pct, external: false},true,true));
		}
		
		public function onTrackDown(e:MouseEvent):void {
			_offset = _thumb.width*0.5;
			var pct:Number = (_observableComponent.mouseX-_offset)/(_trackAsset.width - _thumb.width);
			if (pct < 0) pct = 0;
			if (pct > 1) pct = 1;
			setValue(pct);
			_isDragging = true;
			_observableComponent.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove,false,0,true);
			_observableComponent.stage.addEventListener(MouseEvent.MOUSE_UP, onUp,false,0,true);
			_externalControl = false;
		}
		
		public function get dragging():Boolean {
			return _isDragging;
		}
		
		public function loop(e:TimerEvent):void {
			if (!_isDragging || !_thumb.stage) return;
			_lastX = _currentX;
			_currentX = _thumb.stage.mouseX;
			_vx = _currentX - _lastX;
			if(_thumb.x <= _bounds.left)
			{
				_thumb.x  = _bounds.left;
				_vx *= -1;
			}
			else if(_thumb.x >= _bounds.right)
			{
				_thumb.x = _bounds.right;
				_vx *= -1;
			}
			
			_vx *= 0.45;
			if(Math.abs(_vx) < 0.5) _vx = 0;
			
			var currentPercent:Number = percent;
			if(_lastPercent != currentPercent){
				dispatchEvent(new ComponentEvent(ComponentEvent.VALUE_UPDATE,{value: currentPercent, external: _externalControl},true,true));
			}
			
			_lastPercent = percent;
			
			if(_vx == 0) _externalControl = false;
		}

		
		/**
		 * 
		 * @return 
		 * 
		 */		
		public function get percent():Number{
			return (_thumb.x-_bounds.left)/(_bounds.right-_bounds.left);
		}
	}
}